# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: site-packages\psycopg2\_json.py
"""Implementation of the JSON adaptation objects

This module exists to avoid a circular import problem: pyscopg2.extras depends
on psycopg2.extension, so I can't create the default JSON typecasters in
extensions importing register_json from extras.
"""
import sys
from psycopg2._psycopg import ISQLQuote, QuotedString
from psycopg2._psycopg import new_type, new_array_type, register_type

if sys.version_info[:2] >= (2, 6):
    import json
else:
    try:
        import simplejson as json
    except ImportError:
        json = None

    JSON_OID = 114
    JSONARRAY_OID = 199
    JSONB_OID = 3802
    JSONBARRAY_OID = 3807

    class Json(object):
        """
        An `~psycopg2.extensions.ISQLQuote` wrapper to adapt a Python object to
        :sql:`json` data type.
        
        `!Json` can be used to wrap any object supported by the provided *dumps*
        function.  If none is provided, the standard :py:func:`json.dumps()` is
        used (`!simplejson` for Python < 2.6;
        `~psycopg2.extensions.ISQLQuote.getquoted()` will raise `!ImportError` if
        the module is not available).
        
        """

        def __init__(self, adapted, dumps=None):
            self.adapted = adapted
            self._conn = None
            if dumps is not None:
                self._dumps = dumps
            else:
                if json is not None:
                    self._dumps = json.dumps
                else:
                    self._dumps = None

        def __conform__(self, proto):
            if proto is ISQLQuote:
                return self

        def dumps(self, obj):
            """Serialize *obj* in JSON format.
            
            The default is to call `!json.dumps()` or the *dumps* function
            provided in the constructor. You can override this method to create a
            customized JSON wrapper.
            """
            dumps = self._dumps
            if dumps is not None:
                return dumps(obj)
            raise ImportError(
                "json module not available: you should provide a dumps function"
            )

        def prepare(self, conn):
            self._conn = conn

        def getquoted(self):
            s = self.dumps(self.adapted)
            qs = QuotedString(s)
            if self._conn is not None:
                qs.prepare(self._conn)
            return qs.getquoted()

        if sys.version_info < (3,):

            def __str__(self):
                return self.getquoted()

        else:

            def __str__(self):
                return self.getquoted().decode("ascii", "replace")

    def register_json(
        conn_or_curs=None,
        globally=False,
        loads=None,
        oid=None,
        array_oid=None,
        name="json",
    ):
        """Create and register typecasters converting :sql:`json` type to Python objects.
        
        :param conn_or_curs: a connection or cursor used to find the :sql:`json`
            and :sql:`json[]` oids; the typecasters are registered in a scope
            limited to this object, unless *globally* is set to `!True`. It can be
            `!None` if the oids are provided
        :param globally: if `!False` register the typecasters only on
            *conn_or_curs*, otherwise register them globally
        :param loads: the function used to parse the data into a Python object. If
            `!None` use `!json.loads()`, where `!json` is the module chosen
            according to the Python version (see above)
        :param oid: the OID of the :sql:`json` type if known; If not, it will be
            queried on *conn_or_curs*
        :param array_oid: the OID of the :sql:`json[]` array type if known;
            if not, it will be queried on *conn_or_curs*
        :param name: the name of the data type to look for in *conn_or_curs*
        
        The connection or cursor passed to the function will be used to query the
        database and look for the OID of the :sql:`json` type (or an alternative
        type if *name* if provided). No query is performed if *oid* and *array_oid*
        are provided.  Raise `~psycopg2.ProgrammingError` if the type is not found.
        
        """
        if oid is None:
            oid, array_oid = _get_json_oids(conn_or_curs, name)
        JSON, JSONARRAY = _create_json_typecasters(
            oid, array_oid, loads=loads, name=name.upper()
        )
        register_type(JSON, not globally and conn_or_curs or None)
        if JSONARRAY is not None:
            register_type(JSONARRAY, not globally and conn_or_curs or None)
        return (JSON, JSONARRAY)

    def register_default_json(conn_or_curs=None, globally=False, loads=None):
        """
        Create and register :sql:`json` typecasters for PostgreSQL 9.2 and following.
        
        Since PostgreSQL 9.2 :sql:`json` is a builtin type, hence its oid is known
        and fixed. This function allows specifying a customized *loads* function
        for the default :sql:`json` type without querying the database.
        All the parameters have the same meaning of `register_json()`.
        """
        return register_json(
            conn_or_curs=conn_or_curs,
            globally=globally,
            loads=loads,
            oid=JSON_OID,
            array_oid=JSONARRAY_OID,
        )

    def register_default_jsonb(conn_or_curs=None, globally=False, loads=None):
        """
        Create and register :sql:`jsonb` typecasters for PostgreSQL 9.4 and following.
        
        As in `register_default_json()`, the function allows to register a
        customized *loads* function for the :sql:`jsonb` type at its known oid for
        PostgreSQL 9.4 and following versions.  All the parameters have the same
        meaning of `register_json()`.
        """
        return register_json(
            conn_or_curs=conn_or_curs,
            globally=globally,
            loads=loads,
            oid=JSONB_OID,
            array_oid=JSONBARRAY_OID,
            name="jsonb",
        )

    def _create_json_typecasters(oid, array_oid, loads=None, name="JSON"):
        """Create typecasters for json data type."""
        if loads is None:
            if json is None:
                raise ImportError("no json module available")
            else:
                loads = json.loads

            def typecast_json(s, cur):
                if s is None:
                    return
                else:
                    return loads(s)

            JSON = new_type((oid,), name, typecast_json)
            if array_oid is not None:
                JSONARRAY = new_array_type((array_oid,), "%sARRAY" % name, JSON)
            else:
                JSONARRAY = None
            return (JSON, JSONARRAY)

    def _get_json_oids(conn_or_curs, name="json"):
        from psycopg2.extensions import STATUS_IN_TRANSACTION
        from psycopg2.extras import _solve_conn_curs

        conn, curs = _solve_conn_curs(conn_or_curs)
        conn_status = conn.status
        typarray = conn.server_version >= 80300 and "typarray" or "NULL"
        curs.execute(
            "SELECT t.oid, %s FROM pg_type t WHERE t.typname = %%s;" % typarray, (name,)
        )
        r = curs.fetchone()
        if conn_status != STATUS_IN_TRANSACTION:
            if not conn.autocommit:
                conn.rollback()
        if not r:
            raise conn.ProgrammingError("%s data type not found" % name)
        return r
